using System;
using System.Collections.Generic;
using System.Text;
using Microsoft.CSharp;
using System.CodeDom;
using System.CodeDom.Compiler;
using System.Globalization;

namespace SSOConfigTool.CodeGen
{
    /// <summary>
    /// CodeDom helper class
    /// </summary>
    public static class CodeDomHelper
    {
        private static CSharpCodeProvider _codeProvider;

        private static CSharpCodeProvider CodeProvider
        {
            get
            {
                if (_codeProvider == null)
                {
                    _codeProvider = new CSharpCodeProvider();
                }
                return _codeProvider;
            }
        }

        public static CodeMemberField GetField(string fieldName, Type fieldType)
        {
            return GetField(fieldName, fieldType, null, 0);
        }

        public static CodeMemberField GetField(string fieldName, Type fieldType, MemberAttributes attributes)
        {
            return GetField(fieldName, fieldType, null, attributes);
        }

        public static CodeMemberField GetField(string fieldName, Type fieldType, object initialValue)
        {
            return GetField(fieldName, fieldType, initialValue, 0);
        }

        public static CodeMemberField GetField(string fieldName, Type fieldType, object initialValue, MemberAttributes attributes)
        {
            CodeMemberField field = new CodeMemberField(fieldType, fieldName);
            if (initialValue != null)
            {
                field.InitExpression = new CodePrimitiveExpression(initialValue);
            }
            if (attributes != 0)
            {
                field.Attributes = attributes;
            }
            return field;
        }

        public static CodeMemberProperty GetProperty(string propertyName, string fieldName, Type propertyType)
        {
            return GetProperty(propertyName, fieldName, propertyType, MemberAttributes.Public);
        }

        public static CodeMemberProperty GetProperty(string propertyName, string fieldName, Type propertyType, MemberAttributes attributes)
        {
            return GetProperty(propertyName, fieldName, propertyType, attributes, true);
        }

        public static CodeMemberProperty GetProperty(string propertyName, string fieldName, Type propertyType, MemberAttributes attributes, bool addDefaultAccessors)
        {
            CodeMemberProperty property = new CodeMemberProperty();
            property.Attributes = attributes;
            property.Name = propertyName;
            property.Type = new CodeTypeReference(propertyType);
            if (addDefaultAccessors)
            {
                property.GetStatements.Add(new CodeMethodReturnStatement(new CodeFieldReferenceExpression(null, fieldName)));
                property.SetStatements.Add(new CodeAssignStatement(new CodeFieldReferenceExpression(null, fieldName), new CodePropertySetValueReferenceExpression()));
            }
            return property;
        }

        public static CodeMemberProperty GetProperty(string propertyName, string fieldName, Type propertyType, CodeSnippetExpression getExpression, CodeSnippetExpression setExpression)
        {
            return GetProperty(propertyName, fieldName, propertyType, MemberAttributes.Public, getExpression, setExpression);
        }

        public static CodeMemberProperty GetProperty(string propertyName, string fieldName, Type propertyType, MemberAttributes attributes, CodeSnippetExpression getExpression, CodeSnippetExpression setExpression)
        {
            CodeMemberProperty property = GetProperty(propertyName, fieldName, propertyType, attributes, false);
            if (getExpression != null)
            {
                property.GetStatements.Add(getExpression);
            }
            if (setExpression != null)
            {
                property.SetStatements.Add(setExpression);
            }
            return property;
        }

        public static CodeMemberField GetStringConstant(string name, string value)
        {
            //create string field:
            CodeMemberField constField = new CodeMemberField(typeof(string), GetValidId(name));
            constField.InitExpression = new CodePrimitiveExpression(value);
            //set to public and constant:
            constField.Attributes = (constField.Attributes & ~MemberAttributes.AccessMask & ~MemberAttributes.ScopeMask) | MemberAttributes.Public | MemberAttributes.Const;
            return constField;
        }

        public static CodeCommentStatementCollection GetSummaryComments(string additionalText)
        {
            CodeCommentStatementCollection coll = new CodeCommentStatementCollection();
            coll.Add(new CodeCommentStatement("<summary>", true));
            if (!string.IsNullOrEmpty(additionalText))
            {
                coll.Add(new CodeCommentStatement(additionalText, true));
            }
            coll.Add(new CodeCommentStatement("</summary>", true));
            return coll;
        }

        public static string GetValidId(string idName)
        {
            string result = idName;
            UnicodeCategory category;
            int ordinal = 0;

            if (!CodeProvider.IsValidIdentifier(result))
            {
                //try and make the name valid:
                result = idName.Replace(' ', '_');
                if (!(CodeProvider.IsValidIdentifier(result)))
                {
                    ordinal = 1;
                    while (ordinal < result.Length)
                    {
                        category = Char.GetUnicodeCategory(result[ordinal]);
                        if (category != UnicodeCategory.UppercaseLetter &&
                          UnicodeCategory.LowercaseLetter != category &&
                          UnicodeCategory.TitlecaseLetter != category &&
                          UnicodeCategory.ModifierLetter != category &&
                          UnicodeCategory.OtherLetter != category &&
                          UnicodeCategory.LetterNumber != category &&
                          UnicodeCategory.NonSpacingMark != category &&
                          UnicodeCategory.SpacingCombiningMark != category &&
                          UnicodeCategory.DecimalDigitNumber != category &&
                          UnicodeCategory.ConnectorPunctuation != category)
                        {
                            result = result.Replace(result[ordinal], '_');
                        }
                        ordinal++;
                    }
                }
                //error if not valid:
                if (!CodeProvider.IsValidIdentifier(result))
                {
                    throw new ApplicationException(string.Format("Invalid identifier: {0}, could not make valid: {1}", idName, result));
                }
            }
            return result;
        }
    }
}
